/*
 * beacon.c - Bluetooth beacon operation
 */
#include "bpcommon.h"
/**
 * functions required by read_remote_eir()
 * from storage.c
 */
static int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
{
	return snprintf(buf, size, "%s/%s/%s", path, address, name);
}

/**
 * functions required by read_remote_eir()
 * from textfile.c
 */
static inline int create_filename(char *buf, size_t size,
				const bdaddr_t *bdaddr, const char *name)
{
	char addr[18];

	ba2str(bdaddr, addr);

	return create_name(buf, size, STORAGEDIR, addr, name);
}

/**
 * functions required by read_remote_eir()
 * from textfile.c
 */
static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
{
        char *ptr = map;
        size_t ptrlen = size;

        while (ptrlen > len + 1) {
                int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
                if (cmp == 0) {
                        if (ptr == map)
                                return ptr;

                        if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
                                                        *(ptr + len) == ' ')
                                return ptr;
                }

                if (icase) {
                        char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
                        char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);

                        if (!p1)
                                ptr = p2;
                        else if (!p2)
                                ptr = p1;
                        else
                                ptr = (p1 < p2) ? p1 : p2;
                } else
                        ptr = memchr(ptr + 1, *key, ptrlen - 1);

                if (!ptr)
                        return NULL;

                ptrlen = size - (ptr - map);
        }

        return NULL;
}

/**
 * functions required by read_remote_eir()
 * from textfile.c
 */
static char *strnpbrk(const char *s, ssize_t len, const char *accept)
{
        const char *p = s;
        const char *end;

        end = s + len - 1;

        while (p <= end && *p) {
                const char *a = accept;

                while (*a) {
                        if (*p == *a)
                                return (char *) p;
                        a++;
                }

                p++;
        }

        return NULL;
}

/**
 * functions required by read_remote_eir()
 * from textfile.c
 */
static char *read_key(const char *pathname, const char *key, int icase)
{
	struct stat st;
	char *map, *off, *end, *str = NULL;
	off_t size; size_t len;
	int fd, err = 0;

	fd = open(pathname, O_RDONLY);
	if (fd < 0)
		return NULL;

	if (flock(fd, LOCK_SH) < 0) {
		err = errno;
		goto close;
	}

	if (fstat(fd, &st) < 0) {
		err = errno;
		goto unlock;
	}

	size = st.st_size;

	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
	if (!map || map == MAP_FAILED) {
		err = errno;
		goto unlock;
	}

	len = strlen(key);
	off = find_key(map, size, key, len, icase);
	if (!off) {
		err = EILSEQ;
		goto unmap;
	}

	end = strnpbrk(off, size - (map - off), "\r\n");
	if (!end) {
		err = EILSEQ;
		goto unmap;
	}

	str = malloc(end - off - len);
	if (!str) {
		err = EILSEQ;
		goto unmap;
	}

	memset(str, 0, end - off - len);
	strncpy(str, off + len + 1, end - off - len - 1);

unmap:
	munmap(map, size);

unlock:
	flock(fd, LOCK_UN);

close:
	close(fd);
	errno = err;

	return str;
}

/**
 * functions required by read_remote_eir()
 * from textfile.c
 */
static char *textfile_get(const char *pathname, const char *key)
{
	return read_key(pathname, key, 0);
}

/**
 * read_remote_eir - read and save eir data in string according to remote address
 * from textfile.c
 * @local: address of local adapter
 * @peer: address of devices been discovered 
 * @data: pointer to where eir string should be stored
 * Returns: 0 on success
 */
int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data)
{
	char filename[PATH_MAX + 1], addr[18], *str;
	int i;

	create_filename(filename, PATH_MAX, local, "eir");

	ba2str(peer, addr);

	str = textfile_get(filename, addr);
	if (!str)
		return -ENOENT;

	if (!data) {
		free(str);
		return 0;
	}

	if (strlen(str) < 480) {
		free(str);
		return -EIO;
	}

	for (i = 0; i < 240; i++)
		sscanf(str + (i * 2), "%02hhX", &data[i]);

	free(str);

	return 0;
}

/**
 * btbcon_encode - encode location information and wifi fingerprints into ACSII compatible format
 *       octets       0---3-----10------17-------24------
 * name format: #$#  location rssi bind 1 rssi bind 2   ...
 * @name: array where the name is stored
 * @loc: location information
 * @head: head element of rssi scan list
 * Returns: length of data encoded
 */
static int btbcon_encode(char *name, struct rssi_fgpt *localfgpt)
{
	int locsize, datasize;
	char *pos = name;
	struct rssi_bind *currentbind = localfgpt->bind_head;

	printf("name_encode\n");
	/* special identifier */
	*pos++ = '#';		/*0x23*/
	*pos++ = '$';		/*0x24*/
	*pos++ = '#';		/*0x23*/
	
	/* location information, 10 characters maximum */
	locsize = strlen(localfgpt->loc);
	if (locsize < 10){
		memcpy(pos, localfgpt->loc, locsize);
		memset(pos + locsize, 46, 10 - locsize);	/* use '.' to fill the gaps */
	}else{
		memcpy(pos, localfgpt->loc, 10);
	}
	pos += 10; 

	/* fingerprint information, 8 bytes per element */
	while (currentbind->sample_cnt != 0 ){
		if(currentbind->mean_db != INVALID_RSSI){
			memcpy(pos, currentbind->hashaddr, HASH_ADDR_LEN);
			pos += HASH_ADDR_LEN;
			memset(pos, currentbind->mean_db + 10, 1);
			pos++;
			memset(pos, ceil(currentbind->stddev_db) + 40, 1);
			pos++;
			memset(pos, currentbind->sample_cnt, 1);
			pos++;
		}
		currentbind = currentbind->next; 
	}
	
	datasize = pos - name + 1;
	
	return datasize;
}

/**
 * btbcon_seteir - write acsii name to eir data
 * @loc: location information
 * @head: head element of rssi scan list
 * Returns: 0 on success
 */
static int btbcon_seteir(struct rssi_fgpt *localfgpt)
{
	char data[240];
	uint8_t fec = 0;
	int dd;
	
	printf("name_set\n");	
	dd = hci_open_dev(hci_get_route(NULL));	
	if (dd < 0){
		return -1; 
	}
	
	memset(data, 0, sizeof(data));
	data[1] = 0x2F;
	data[0] = btbcon_encode(data+2, localfgpt);
	printf("hci_write_eir\n");
	if (hci_write_ext_inquiry_response(dd, fec, (uint8_t *)data, 2000) < 0) {
		printf("hci_write_eir_error\n");
		return -1; 
	}

	hci_close_dev(dd);
	
	return 0;
}

/**
 * btbcon_prompt - prompt user for location input
 *  				  before setting up beacon site perform a full wi-fi scan if it haven't done yet
 * @struct rssi_map *rssimap: global rssi map structure
 * Returns: 0 on success
 */
int btbcon_prompt(struct rssi_map *rssimap)
{
	int input, idx;
	struct rssi_fgpt *localfgpt = rssimap->fgpt_head;
	printf("Enter location: ");
	idx = 0;
	while ((input = getchar()) != '\n'){
		myposition[idx++] = input;
	}
	myposition[idx] = '\0';

	if (rssimap->globalconfig->shortscan)
		scanner_wifi(rssimap, 0);
	strcpy(localfgpt->loc, myposition);

	if (rssimap->globalconfig->setbcon){
		/* TODO: open bluetooth interface */
		btbcon_seteir(localfgpt);
	}else {
		/* TODO: flush eir data */
	}

	return 0; 
}
